// This code was mostly taken from Alex Benjamin's fivecarddraw implementation.
// It is modified in certain instances to make the five card draw auto discard
// logic be consistent with the CSE571 lab specifications.
//
// Author: Jonathan Wald (Alex Benjamin)
//
#include "fivecarddraw.h"
#include "player-game.h"
#include "card.h"
#include "poker.h"
#include "deck.h"
#include <sstream>
#include <vector>
#include <algorithm>
#include <string>
#include <iostream>
#include <fstream>
#include <functional>
using namespace std;

FiveCardDraw::FiveCardDraw() {}

void FiveCardDraw::auto_discardFOAK(Hand &cards){
  //Find the card not in the FOAK and get rid of it
  vector<Card> vec1 = cards.getHand();
  int ranks_above;
  if(vec1[0].c_rank == vec1[1].c_rank){
    ranks_above = 12 - vec1[4].c_rank;
    if (vec1[0].c_rank > vec1[4].c_rank) {
      ranks_above--;
    }
    // 13 total ranks, -1 for both the FOAK and the single.
    // This leaves 11 ranks left for the fifth card, so
    // if there are 6 opportunities to improve than draw
    // another one.
    if (ranks_above > 5) {
      discardDeck.add_card(cards[4]);
      cards.remove_card(4);
    }
  }
  else{
    ranks_above = 12 - vec1[0].c_rank;
    if (vec1[4].c_rank > vec1[0].c_rank) {
      ranks_above--;
    }
    // 13 total ranks, -1 for both the FOAK and the single.
    // This leaves 11 ranks left for the fifth card, so
    // if there are 6 opportunities to improve than draw
    // another one.
    if (ranks_above > 5) {
      discardDeck.add_card(cards[0]);
      cards.remove_card(0);
    }
  }
}
void FiveCardDraw::auto_discardTP(Hand &cards){
  //Find the card not in the two pair and get rid of it
  vector<Card> vec1 = cards.getHand();
  int lp1 = -3, hp1 = -3;
  //Find the two pair ranks from hand
  for(int i = 0; i < 4; ++i){
    if(vec1[i].c_rank == vec1[i+1].c_rank){
      if(lp1 == -3){
	lp1 = vec1[i].c_rank;
	++i;
      }
      else{
	hp1 = vec1[i].c_rank;
	break;
      }
    }
  }

  //find remaining card in hand
  int i = 0;
  for(; i < 5; ++i){
    if(vec1[i].c_rank != lp1 && vec1[i].c_rank != hp1)
      break;
  }
  discardDeck.add_card(cards[i]);
  cards.remove_card(i);
}
void FiveCardDraw::auto_discardTOAK(Hand &cards){
  //Find the two cards not in the TOAK and get rid of them
  vector<Card> vec1 = cards.getHand();
  int rank1;
  //Find the 3oaK value
  for(int i = 0; i < 4; ++i){
    if(vec1[i].c_rank == vec1[i+1].c_rank)
      rank1 = vec1[i].c_rank;
  }

  //find remaining card indexes
  int rem1 = -3, rem2 = -3;
  for(int i = 0; i < 5; ++i){
    if(vec1[i].c_rank != rank1){
      if(rem1 == -3)
	rem1 = i;
      else
	rem2 = i;
    }
  }
  if(rem1 < rem2){
    int swap = rem1;
    rem1 = rem2;
    rem2 = swap;
  }//now rem2 is < rem1. Need this because vector index will change 
  //on erase so we should delete from the highest index to lowest
  //so that indexes will be correct
  discardDeck.add_card(cards[rem1]);
  cards.remove_card(rem1);
  discardDeck.add_card(cards[rem2]);
  cards.remove_card(rem2);
	
}
void FiveCardDraw::auto_discardPair(Hand &cards){
  vector<Card> vec1 = cards.getHand();
  int rank1;
  //obtain which is the pair rank
  for(size_t i = 0; i < vec1.size()-1; ++i){
    if(vec1[i].c_rank == vec1[i+1].c_rank){
      rank1 = vec1[i].c_rank;
      break;
    }
  }
  //Collect the remaining three cards from each hand for discard
  vector<int> firstRanks;
  for(size_t i = 0; i < vec1.size(); ++i){
    if(vec1[i].c_rank != rank1){
      firstRanks.push_back(i);
      //store the indexes of the hand which we will delete
    }
  }
  sort(firstRanks.begin(),firstRanks.end());
  //Need this because vector index will change 
  //on erase so we should delete from the highest index to lowest
  //so that indexes will be correct
  bool jacks_or_higher = rank1 > Card::ten;

  if (jacks_or_higher ||
      cards[firstRanks[2]].c_rank != Card::ace) {
    discardDeck.add_card(cards[firstRanks[2]]);
    cards.remove_card(firstRanks[2]);
  }
  if (jacks_or_higher ||
      cards[firstRanks[1]].c_rank != Card::ace) {
    discardDeck.add_card(cards[firstRanks[1]]);
    cards.remove_card(firstRanks[1]);
  }
  if (jacks_or_higher ||
      cards[firstRanks[0]].c_rank != Card::ace) {
    discardDeck.add_card(cards[firstRanks[0]]);
    cards.remove_card(firstRanks[0]);
  }

}

int FiveCardDraw::flush_draw(Hand &cards) {
  int count = 0;
  int index = -1;

  for(int i = 0; i < 5; ++i) {
    if(cards[i].c_suit == Card::club) ++count;
    else index = i;
  }
  if (count == 4) return index;
  count = 0;
  for(int i = 0; i < 5; ++i) {
    if(cards[i].c_suit == Card::diamond) ++count;
    else index = i;
  }
  if (count == 4) return index;
  count = 0;
  for(int i = 0; i < 5; ++i) {
    if(cards[i].c_suit == Card::heart) ++count;
    else index = i;
  }
  if (count == 4) return index;
  count = 0;
  for(int i = 0; i < 5; ++i) {
    if(cards[i].c_suit == Card::spade) ++count;
    else index = i;
  }
  if (count == 4) return index;

  return -1;
}

int FiveCardDraw::straight_draw(Hand &cards) {
  int skip_index = -1;
  int last = cards[0].c_rank;
  bool broken = false;
  for(int i = 1; i < 5; ++i) {
    if (cards[i].c_rank == last + 1) {
      last = cards[i].c_rank;
    }
    else if (skip_index == -1) skip_index = i;
    else {
      broken = true;
      break;
    }
  }
  if (!broken) return skip_index;
  if (cards[1].c_rank + 1 == cards[2].c_rank &&
      cards[2].c_rank + 1 == cards[3].c_rank &&
      cards[3].c_rank + 1 == cards[4].c_rank) {
    return 0;
  }
  return -1;
}

void FiveCardDraw::auto_discardNoRank(Hand &cards){
  int retval = flush_draw(cards);
  if (retval != -1) {
    discardDeck.add_card(cards[retval]);
    cards.remove_card(retval);
    return;
  }
  retval = straight_draw(cards);
  if (retval != -1) {
    discardDeck.add_card(cards[retval]);
    cards.remove_card(retval);
    return;
  }
	


  //delete all cards except the ace
  for(unsigned int i = 0; i < 4; ++i){
    discardDeck.add_card(cards[0]);
    cards.remove_card(0);		
  }
  if (cards[0].c_rank != Card::ace) {
    discardDeck.add_card(cards[0]);
    cards.remove_card(0);
  }

}
int FiveCardDraw::auto_discard(PlayerGame* p){
  //
  // The auto_discard method gets the rank of the hand for auto_players
  // and then calls an appropriate helper method which will find the
  // appropriate cards to be discarded depending on the type of the hand.
  //
  p->hand.sort_cards();
  int rank = getHandRank(p->hand);
  if(rank == STRAIGHT_FLUSH || rank == FLUSH 
     || rank == FULL_HOUSE || rank == STRAIGHT) {
    return 0;
  }
  else if(rank == FOUR_OF_A_KIND){
    auto_discardFOAK(p->hand);
  }
  else if(rank == TWO_PAIR){
    auto_discardTP(p->hand);
  }
  else if(rank == THREE_OF_A_KIND){
    auto_discardTOAK(p->hand);
  }
  else if(rank == PAIR){
    auto_discardPair(p->hand);
  }
  else if(rank == NO_RANK){
    auto_discardNoRank(p->hand);
  }

  return 0;
}
